package za.org.jcicada.datastore.impl.mongodb;

import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import za.org.jcicada.datastore.IDataStoreClient;
import za.org.jcicada.datastore.impl.DataStoreServerAbstract;
import za.org.jcicada.genss.messages.Result;
import za.org.jcicada.genss.sensor.ISensorValue;
import za.org.jcicada.genss.sensor.SensorStatus;
import za.org.jcicada.genss.sensor.SensorURN;
import za.org.jcicada.genss.sensor.impl.SensorStatusValue;
import za.org.jcicada.genss.sensor.impl.SensorValue;

import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;

public class MongoDBDataStoreServer extends DataStoreServerAbstract {

	private DBCollection sensorColl;
	private DBCollection statusColl;

	@Override
	public void initialize(URI connectionURI) throws Exception {
		MongoDB mongoDB = MongoDB.getInstance(connectionURI);
		sensorColl = mongoDB.getDB().getCollection("sensors");
		statusColl = mongoDB.getDB().getCollection("status");
	}

	@Override
	public void storeSensor(SensorURN sensorURN, ISensorValue value) {
		BasicDBObject doc = new BasicDBObject();

		doc.put("sensor", sensorURN.toString());
		doc.put("time", value.getTime());
		doc.put("value", value.getValue());

		sensorColl.insert(doc);
	}
	
	@Override
	public void storeSensorStatus(SensorURN sensorURN, SensorStatusValue statusValue) {
		BasicDBObject doc = new BasicDBObject();
		
		doc.put("sensor", sensorURN.toString());
		doc.put("time", statusValue.getTime());
		doc.put("status", statusValue.getStatus().toString());
		doc.put("message", statusValue.getMessage());
		
		statusColl.insert(doc);
	}

	public void retrieveSensorHistory(IDataStoreClient client, SensorURN sensorURN, Date from, Date to) {
		List<ISensorValue> values = new ArrayList<ISensorValue>();
		
		// find last value before range
		BasicDBObject query = new BasicDBObject();
		query.put("sensor", sensorURN.toString());
		query.put("time", new BasicDBObject("$lt", from.getTime()));
		DBCursor cursor = sensorColl.find(query).sort(new BasicDBObject("time", -1)).limit(1);
		addValues(values, cursor);
		
		// find the values within the range
		query = new BasicDBObject();
		query.put("sensor", sensorURN.toString());
		query.put("time", new BasicDBObject("$gte", from.getTime()).append("$lte", to.getTime()));  // i.e.   from <= time <= to
		cursor = sensorColl.find(query);//.sort(new BasicDBObject("time", 1));
		addValues(values, cursor);
		
		client.replySensorHistory(new Result(), sensorURN, from, to, values);
	}

	private void addValues(List<ISensorValue> values, DBCursor cursor) {
		while (cursor.hasNext()) {
			DBObject object = cursor.next();
			long time = ((Long) object.get("time")).longValue();
			Object value = (Object) object.get("value");
			
			SensorValue sensorValue = new SensorValue(time, value);
			values.add(sensorValue);
		}
	}

	@Override
	public void retrieveStatusHistory(IDataStoreClient client, SensorURN sensorURN, Date from, Date to) {
		List<SensorStatusValue> values = new ArrayList<SensorStatusValue>();
		
		// find last value before range
		BasicDBObject query = new BasicDBObject();
		query.put("sensor", sensorURN.toString());
		query.put("time", new BasicDBObject("$lt", from.getTime()));
		DBCursor cursor = statusColl.find(query).sort(new BasicDBObject("time", -1)).limit(1);
		addStatuses(values, cursor);
		
		// find the values within the range
		query = new BasicDBObject();
		query.put("sensor", sensorURN.toString());
		query.put("time", new BasicDBObject("$gte", from.getTime()).append("$lte", to.getTime()));  // i.e.   from <= time <= to
		cursor = statusColl.find(query);//.sort(new BasicDBObject("time", 1));
		addStatuses(values, cursor);
		
		client.replyStatusHistory(new Result(), sensorURN, from, to, values);
	}

	private void addStatuses(List<SensorStatusValue> values, DBCursor cursor) {
		while (cursor.hasNext()) {
			DBObject object = cursor.next();
			SensorStatus status = SensorStatus.valueOf(object.get("status").toString());
			long time = ((Long) object.get("time")).longValue();
			String message = (String) object.get("message");
			
			SensorStatusValue statusValue = new SensorStatusValue(status, time, message);
			values.add(statusValue);
		}
	}

}
